package cn.tedu.antcolony.passport.security.iml;

import cn.tedu.antcolony.passport.config.SecurityConfiguration;
import cn.tedu.antcolony.passport.mapper.AdminMapper;
import cn.tedu.antcolony.passport.mapper.AdminRoleMapper;
import cn.tedu.antcolony.passport.pojo.dto.AdminLoginInfoDTO;
import cn.tedu.antcolony.passport.pojo.entity.Admin;
import cn.tedu.antcolony.passport.pojo.entity.AdminLoginLog;
import cn.tedu.antcolony.passport.pojo.entity.AdminRole;
import cn.tedu.antcolony.passport.pojo.param.*;
import cn.tedu.antcolony.passport.pojo.vo.*;
import cn.tedu.antcolony.passport.security.MySecurityUser;
import cn.tedu.antcolony.passport.service.IAdminLoginLogService;
import cn.tedu.antcolony.passport.service.IAdminService;

import cn.tedu.antcolonycommons.consts.AdminInfoCacheConsts;
import cn.tedu.antcolonycommons.consts.HttpConsts;
import cn.tedu.antcolonycommons.consts.JwtCacheConsts;
import cn.tedu.antcolonycommons.ex.ServiceException;
import cn.tedu.antcolonycommons.pojo.po.AdminLoginInfoPO;
import cn.tedu.antcolonycommons.pojo.vo.PageData;
import cn.tedu.antcolonycommons.util.JwtUtils;
import cn.tedu.antcolonycommons.util.PageInfoToPageDataConverter;
import cn.tedu.antcolonycommons.util.RedisTemplateUtils;
import cn.tedu.antcolonycommons.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 管理员实现类
 */
@Service
@Slf4j
/**
 * 开启事务 isolation = Isolation.READ_COMMITTED 事务的隔离级别(读已提交) Propagation.REQUIRED事务的传播性  rollbackFor = Exception.class 异常回滚
 */
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class AdminServiceImpl implements IAdminService {
    /**
     * 密钥
     */
    @Value("${antcolony.jwt.secret-key}")
    String secretKey;
    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private SecurityConfiguration securityConfiguration;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Resource
    private JwtUtils jwtUtils;
    @Autowired
    private IAdminLoginLogService iAdminLoginLogService;
    /**
     * 过期时间 分钟
     */
    @Value("${antcolony.jwt.duration-in-minute}")
    private Long durationInMinute;

    /**
     * Redis 工具类
     */
    @Autowired
    private RedisTemplateUtils redisTemplateUtils;

    /**
     * 添加管理员
     *
     * @param adminAddNewParam
     */
    @Override
    public void addNew(AdminAddNewParam adminAddNewParam) {
        log.debug("开始处理【添加管理员业务】,参数：{}", adminAddNewParam);
        /**
         * 检查用户名是否重复
         */
        AdminStandardVO adminByName = adminMapper.getAdminByName(adminAddNewParam.getUsername());
        if (adminByName != null) {
            String message = "添加失败,用户名不能重复!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        /**
         * 检查手机号是否重复
         */
        AdminStandardVO adminByPhone = adminMapper.getAdminByPhone(adminAddNewParam.getPhone());
        if (adminByPhone != null) {
            String message = "添加失败,手机号不能重复!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        /**
         * 检查邮箱是否重复
         */
        AdminStandardVO adminByEmail = adminMapper.getAdminByEmail(adminAddNewParam.getEmail());
        if (adminByEmail != null) {
            String message = "添加失败,邮箱不能重复!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        /**
         * 开始执行添加管理员
         */
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewParam, admin);
        //给密码加密
        BCryptPasswordEncoder bCryptPasswordEncoder = (BCryptPasswordEncoder) securityConfiguration.passwordEncoder();
        String encode = bCryptPasswordEncoder.encode(admin.getPassword());
        admin.setPassword(encode);
        //数据创建时间
        admin.setGmtCreate(LocalDateTime.now());
        admin.setGmtModified(LocalDateTime.now());
        log.debug("开始执行,添加管理员");
        int insert = adminMapper.insert(admin);
        if (insert < 1) {
            String message = "添加失败,系统异常!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        /**
         * 添加管理员角色
         */
        Long[] adminRoles = adminAddNewParam.getAdminRoles();
        List<AdminRole> roles = new ArrayList<>();
        for (int i = 0; i < adminRoles.length; i++) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(adminRoles[i]);
            adminRole.setGmtCreate(LocalDateTime.now());
            adminRole.setGmtModified(LocalDateTime.now());
            roles.add(adminRole);
        }
        log.debug("开始添加管理员权限");
        int result = adminRoleMapper.insertBatch(roles);
        if (result != adminRoles.length) {
            String message = "添加失败,系统异常!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }

    /**
     * 修改密码
     */
    @Override
    public void updatePassword(AdminUpdatePassword updatePassword, Long id) {
        /**
         * 检查确定密码是否正确
         */
        updatePassword.setId(id);
        if (!updatePassword.getNewPassword().equals(updatePassword.getConfirmPassword())) {
            String message = "修改密码失败，确认密码错误!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        /**
         * 判断该用户名称和密码是否正确
         */
        //TODO 根据id和名称查询 管理员账号是否存在
        AdminStandardVO adminById = adminMapper.getAdminById(updatePassword.getId());
        if (adminById == null) {
            String message = "修改密码失败，该用户不存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        //TODO 判断输入的密码是否正确
        BCryptPasswordEncoder bCryptPasswordEncoder = (BCryptPasswordEncoder) securityConfiguration.passwordEncoder();
//        boolean matches = bCryptPasswordEncoder.matches(updatePassword.getOldPassword(), adminById.getPassword());
//        if (!matches){
//                String message="修改密码失败，该用户名称密码错误!";
//                log.warn(message);
//                throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
//        }

        /**
         * 验证正确
         */
        Admin admin = new Admin();
        //给新密码加密
        String encodeNew = bCryptPasswordEncoder.encode(updatePassword.getNewPassword());
        admin.setPassword(encodeNew);
        //设置最后修改数据时间
        admin.setGmtModified(LocalDateTime.now());
        //修改行数据的 id 和 用户名
        admin.setId(updatePassword.getId());
        log.debug("开始执行修改密码,参数:{}", updatePassword);
        int result = adminMapper.updatePassword(admin);
        if (result < 1) {
            String message = "修改密码失败，系统异常!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }

    /**
     * 查询管理员列表
     *
     * @param pageNum  当前页码数
     * @param pageSize 每页查询条数
     * @return
     */
    @Override
    public PageData<AdminListItemVO> adminList(Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<AdminListItemVO> adminListItemVOS = adminMapper.adminLIst();
        PageInfo<AdminListItemVO> listItemVOPageInfo = new PageInfo<>(adminListItemVOS);
        PageData<AdminListItemVO> converter = PageInfoToPageDataConverter.convert(listItemVOPageInfo);
        return converter;
    }

    /**
     * 查询管理员列表
     *
     * @param pageNum AdminInfoCacheConsts.PAGESIZE 每页5条
     * @return
     */
    @Override
    public PageData<AdminListItemVO> adminList(Integer pageNum) {
        return adminList(pageNum, AdminInfoCacheConsts.PAGESIZE);
    }

    /**
     * 根据id删除管理员
     *
     * @param id
     */
    @Override
    public void deleteAdminById(Long id) {
        //为该 id 的管理员是否存在
        AdminStandardVO adminById = adminMapper.getAdminById(id);
        if (adminById == null) {
            String message = "删除失败，该管理员不存在!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        //存在，在管理员表中删除
        Integer integer = adminMapper.deleteAdminById(id);
        if (integer != 1) {
            String message = "删除管理员失败,系统异常!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UNKNOWN, message);
        }
        //在角色关联表中 删除该对象对应的角色关联信息
        Integer deleteAdminAndRoleByAdminId = adminMapper.deleteAdminAndRoleByAdminId(id);
        if (deleteAdminAndRoleByAdminId != 1) {
            String message = "删除管理员失败，系统异常!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UNKNOWN, message);
        }
    }


    /**
     * 管理员登录
     *
     * @param adminLoginParam
     * @param remoteAddr      ip地址
     * @param userAgent       客户端登录浏览器
     */

    @Override
    public AdminLoginInfoDTO adminLogin(AdminLoginParam adminLoginParam, String remoteAddr, String userAgent) {
        log.debug("开始处理管理员登录业务，参数:{}", adminLoginParam);
        Authentication authentication = new UsernamePasswordAuthenticationToken(adminLoginParam.getUsername(), adminLoginParam.getPassword());
        Authentication authenticateResult = authenticationManager.authenticate(authentication);
        log.debug("登录验证完成,认证结果:{}", authenticateResult);
        //从认证结果中取出所需的数据
        Object principal = authenticateResult.getPrincipal();
        MySecurityUser mySecurityUser = (MySecurityUser) principal;

        //登录成功，修改管理员的 登录ip 登录次数  登录时间
        UpdateLoginfoParam updateLoginfoParam = UpdateLoginfoParam.builder().adminId(mySecurityUser.getUserId())
                .lastLoginIp(remoteAddr).gmtLastLogin(LocalDateTime.now()).username(mySecurityUser.getUsername()).build();
        Integer updateLoginIpAndLoginCount = adminMapper.updateLoginIpAndLoginCount(updateLoginfoParam);

        //管理员登录成功,记录登录日志
        AdminLoginLog adminLoginLog = AdminLoginLog.builder().adminId(mySecurityUser.getUserId())
                .username(mySecurityUser.getUsername()).nickname(mySecurityUser.getAdminLoginInfoVo().getNickname())
                .ip(remoteAddr).userAgent(userAgent).gmtLogin(LocalDateTime.now()).gmtCreate(LocalDateTime.now())
                .gmtModified(LocalDateTime.now()).build();
        iAdminLoginLogService.addNewAdminLoginLog(adminLoginLog);
        //payload
        AdminLoginInfoVo adminLoginInfoVo = mySecurityUser.getAdminLoginInfoVo();
        List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) mySecurityUser.getAuthorities();

        //用户 权限 JSON 字符串
        String authoritiestoJSONString = JSON.toJSONString(authorities);
        Map<String, Object> claims = new HashMap<>();
        //当事人对象
        //String writeValueAsString = objectMapper.writeValueAsString(adminLoginInfoVo);
        //id
        claims.put(AdminInfoCacheConsts.ADMIN_ID, adminLoginInfoVo.getId());
        // username
        claims.put(AdminInfoCacheConsts.ADMIN_USERNAME, adminLoginInfoVo.getUsername());
        //用户权限
        //claims.put("userAuth",list);
        //过期时间 120分钟 redis中 key过期时间
        Long tokenDuration = durationInMinute * 60L * 1000;
        //前端 Jwt过期时间
        Date tokenexpireDate = new Date(System.currentTimeMillis() + tokenDuration);
        //生成Jwt
        String jwt = jwtUtils.createJwt(claims, tokenexpireDate);
        log.debug("生成此管理员的信息对应的JWT:{}", jwt);

        // 需要缓存到Redis中的数据
        AdminLoginInfoPO adminLoginInfoPO = AdminLoginInfoPO.builder()
                .id(adminLoginInfoVo.getId())
                .enable(adminLoginInfoVo.getEnable())
                .remoteAddr(remoteAddr)
                .userAgent(userAgent)
                .authorityListJsonString(authoritiestoJSONString).build();
        redisTemplateUtils.set(JwtCacheConsts.ADMIN_JWT_PREFIX + jwt, adminLoginInfoPO, tokenDuration);

        //登录成功 返回前端 登录成功对象
        AdminLoginInfoDTO adminLoginInfoDTO = AdminLoginInfoDTO.builder()
                .id(adminLoginInfoVo.getId())
                .username(adminLoginInfoVo.getUsername())
                .nickname(adminLoginInfoVo.getNickname())
                .avatar(adminLoginInfoVo.getAvatar())
                .token(jwt)
                .tokenExpiration(tokenexpireDate).build();
        return adminLoginInfoDTO;
    }

    /**
     * 管理员退出登录
     */
    @Override
    public void adminLogOut(String jwtToken) {
        //String authorization = request.getHeader(HttpConsts.HEADER_AUTHORIZATION);
        String jwt = jwtToken.replace(HttpConsts.AUTHORIZATION_BEARER, "");
        redisTemplateUtils.delete(JwtCacheConsts.ADMIN_JWT_PREFIX + jwt);
    }

    /**
     * 修改管理员基本信息类
     *
     * @param updateAdminParam
     */
    @Override
    public void updateAdmin(UpdateAdminParam updateAdminParam, Long id) {
        updateAdminParam.setId(id);
        //判断该用户存不存在
        AdminStandardVO adminById = adminMapper.getAdminById(updateAdminParam.getId());
        if (adminById == null) {
            String message = "修改失败，该用户不存在!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        //判断修改后的用户名是否与其他用户名冲突
        if (StringUtils.isNotBlank(updateAdminParam.getUsername()) &&
                !adminById.getUsername().equals(updateAdminParam.getUsername())) {
            AdminStandardVO adminByName = adminMapper.getAdminByName(updateAdminParam.getUsername());
            if (adminByName != null) {
                String message = "修改失败，用户名重复!";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
            }

        }
        //判断修改的手机号是否唯一
        if (StringUtils.isNotBlank(updateAdminParam.getPhone()) &&
                !adminById.getPhone().equals(updateAdminParam.getPhone())) {
            AdminStandardVO adminByPhone = adminMapper.getAdminByPhone(updateAdminParam.getPhone());
            if (adminByPhone != null) {
                String message = "修改失败，手机号重复!";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
            }
        }
        //判断修改邮箱是否重复
        if (StringUtils.isNotBlank(updateAdminParam.getEmail()) &&
                !adminById.getEmail().equals(updateAdminParam.getEmail())) {
            AdminStandardVO adminByEmail = adminMapper.getAdminByEmail(updateAdminParam.getEmail());
            if (adminByEmail != null) {
                String message = "修改失败，邮箱重复!";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
            }
        }
        //开始修改管理员信息
        Admin admin = new Admin();
        BeanUtils.copyProperties(updateAdminParam, admin);
        admin.setGmtModified(LocalDateTime.now());
        Integer updateAdminById = adminMapper.updateAdminById(admin);
//            if (updateAdminById!=1){
//                String message="修改失败，系统异常!";
//                log.warn(message);
//                throw new ServiceException(ServiceCode.ERR_UNKNOWN,message);
//            }

    }

    @Override
    public AdminStandardVO getAdminById(Long id) {
        AdminStandardVO adminById = adminMapper.getAdminById(id);
        if (adminById == null) {
            String message = "修改失败，用户不存在!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        return adminById;
    }
}

